package com.eshop.modules.system.rest;

import com.eshop.tools.domain.VerificationCode;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.eshop.modules.system.domain.vo.UserPassVo;
import org.springframework.web.bind.annotation.DeleteMapping;
import java.util.Iterator;
import java.util.Collections;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.function.Function;
import com.eshop.modules.system.service.dto.RoleSmallDto;
import com.eshop.exception.BadRequestException;
import com.eshop.utils.SecurityUtils;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.eshop.modules.aop.ForbidSubmit;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.validation.annotation.Validated;
import com.eshop.modules.system.domain.User;
import java.util.Set;
import com.eshop.utils.PageUtil;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import org.springframework.util.ObjectUtils;
import java.util.HashSet;
import org.springframework.http.ResponseEntity;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import io.swagger.annotations.ApiOperation;
import com.eshop.logging.aop.log.Log;
import java.io.IOException;
import java.util.List;
import com.eshop.modules.system.service.dto.UserDto;
import com.eshop.modules.system.service.dto.UserQueryCriteria;
import javax.servlet.http.HttpServletResponse;
import com.eshop.dozer.service.IGenerator;
import com.eshop.tools.service.VerificationCodeService;
import com.eshop.modules.system.service.RoleService;
import com.eshop.modules.system.service.DeptService;
import com.eshop.config.DataScope;
import com.eshop.modules.system.service.UserService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;

@Api(tags = { "系统：用户管理" })
@RestController
@RequestMapping({ "/api/users" })
public class SysUserController
{
    @Value("${rsa.private_key}")
    private String privateKey;
    private final PasswordEncoder passwordEncoder;
    private final UserService userService;
    private final DataScope dataScope;
    private final DeptService deptService;
    private final RoleService roleService;
    private final VerificationCodeService verificationCodeService;
    private final IGenerator generator;
    
    public SysUserController(final PasswordEncoder passwordEncoder, final UserService userService, final DataScope dataScope, final DeptService deptService, final RoleService roleService, final VerificationCodeService verificationCodeService, final IGenerator generator) {
        this.passwordEncoder = passwordEncoder;
        this.userService = userService;
        this.dataScope = dataScope;
        this.deptService = deptService;
        this.roleService = roleService;
        this.verificationCodeService = verificationCodeService;
        this.generator = generator;
    }
    
    @Log("导出用户数据")
    @ApiOperation("导出用户数据")
    @GetMapping({ "/download" })
    @PreAuthorize("@el.check('admin','user:list')")
    public void download(final HttpServletResponse response, final UserQueryCriteria criteria) throws IOException {
        this.userService.download(this.generator.convert((List)this.userService.queryAll(criteria), (Class)UserDto.class), response);
    }
    
    @Log("查询用户")
    @ApiOperation("查询用户")
    @GetMapping
    @PreAuthorize("@el.check('admin','user:list')")
    public ResponseEntity<Object> getUsers(final UserQueryCriteria criteria, final Pageable pageable) {
        final Set<Long> deptSet = new HashSet<Long>();
        final Set<Long> result = new HashSet<Long>();
        if (!ObjectUtils.isEmpty(criteria.getDeptId())) {
            deptSet.add(criteria.getDeptId());
            deptSet.addAll(this.dataScope.getDeptChildren(this.deptService.findByPid(criteria.getDeptId())));
        }
        final Set<Long> deptIds = this.dataScope.getDeptIds();
        if (CollectionUtils.isEmpty((Collection)deptIds) || CollectionUtils.isEmpty((Collection)deptSet)) {
            result.addAll(deptSet);
            result.addAll(deptIds);
            criteria.setDeptIds(result);
            return new ResponseEntity(this.userService.queryAll(criteria, pageable), HttpStatus.OK);
        }
        result.addAll(deptSet);
        result.retainAll(deptIds);
        criteria.setDeptIds(result);
        if (result.size() == 0) {
            return new ResponseEntity(PageUtil.toPage(null, 0), HttpStatus.OK);
        }
        return new ResponseEntity(this.userService.queryAll(criteria, pageable), HttpStatus.OK);
    }
    
    @ForbidSubmit
    @Log("新增用户")
    @ApiOperation("新增用户")
    @PostMapping
    @PreAuthorize("@el.check('admin','user:add')")
    public ResponseEntity<Object> create(@Validated @RequestBody final User resources) {
        this.checkLevel(resources);
        resources.setPassword(this.passwordEncoder.encode((CharSequence)"123456"));
        return new ResponseEntity(this.userService.create(resources), HttpStatus.CREATED);
    }
    
    @ForbidSubmit
    @Log("修改用户")
    @ApiOperation("修改用户")
    @PutMapping
    @PreAuthorize("@el.check('admin','user:edit')")
    public ResponseEntity<Object> update(@Validated @RequestBody final User resources) {
        this.checkLevel(resources);
        this.userService.update(resources);
        return new ResponseEntity(HttpStatus.NO_CONTENT);
    }
    
    @ForbidSubmit
    @Log("修改用户：个人中心")
    @ApiOperation("修改用户：个人中心")
    @PutMapping({ "center" })
    public ResponseEntity<Object> center(@Validated({ User.Update.class }) @RequestBody final User resources) {
        final UserDto userDto = this.userService.findByName(SecurityUtils.getUsername());
        if (!resources.getId().equals(userDto.getId())) {
            throw new BadRequestException("不能修改他人资料");
        }
        this.userService.saveOrUpdate(resources);
        return new ResponseEntity(HttpStatus.NO_CONTENT);
    }
    
    @ForbidSubmit
    @Log("删除用户")
    @ApiOperation("删除用户")
    @DeleteMapping
    @PreAuthorize("@el.check('admin','user:del')")
    public ResponseEntity<Object> delete(@RequestBody final Set<Long> ids) {
        final UserDto user = this.userService.findByName(SecurityUtils.getUsername());
        for (final Long id : ids) {
            final Integer currentLevel = Collections.min(this.roleService.findByUsersId(user.getId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
            final Integer optLevel = Collections.min(this.roleService.findByUsersId(id).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
            if (currentLevel > optLevel) {
                throw new BadRequestException("角色权限不足，不能删除：" + this.userService.findByName(SecurityUtils.getUsername()).getUsername());
            }
        }
        this.userService.delete(ids);
        return new ResponseEntity(HttpStatus.OK);
    }
    
    @ForbidSubmit
    @ApiOperation("修改密码")
    @PostMapping({ "/updatePass" })
    public ResponseEntity<Object> updatePass(@RequestBody final UserPassVo passVo) {
        final RSA rsa = new RSA(this.privateKey, (String)null);
        final String oldPass = new String(rsa.decrypt(passVo.getOldPass(), KeyType.PrivateKey));
        final String newPass = new String(rsa.decrypt(passVo.getNewPass(), KeyType.PrivateKey));
        final UserDto user = this.userService.findByName(SecurityUtils.getUsername());
        if (!this.passwordEncoder.matches((CharSequence)oldPass, user.getPassword())) {
            throw new BadRequestException("修改失败，旧密码错误");
        }
        if (this.passwordEncoder.matches((CharSequence)newPass, user.getPassword())) {
            throw new BadRequestException("新密码不能与旧密码相同");
        }
        this.userService.updatePass(user.getUsername(), this.passwordEncoder.encode((CharSequence)newPass));
        return new ResponseEntity(HttpStatus.OK);
    }
    
    @ForbidSubmit
    @ApiOperation("修改头像")
    @PostMapping({ "/updateAvatar" })
    public ResponseEntity<Object> updateAvatar(@RequestParam final MultipartFile file) {
        this.userService.updateAvatar(file);
        return new ResponseEntity(HttpStatus.OK);
    }
    
    @ForbidSubmit
    @Log("修改邮箱")
    @ApiOperation("修改邮箱")
    @PostMapping({ "/updateEmail/{code}" })
    public ResponseEntity<Object> updateEmail(@PathVariable final String code, @RequestBody final User user) {
        final RSA rsa = new RSA(this.privateKey, (String)null);
        final String password = new String(rsa.decrypt(user.getPassword(), KeyType.PrivateKey));
        final UserDto userDto = this.userService.findByName(SecurityUtils.getUsername());
        if (!this.passwordEncoder.matches((CharSequence)password, userDto.getPassword())) {
            throw new BadRequestException("密码错误");
        }
        final VerificationCode verificationCode = new VerificationCode(code, "重置邮箱", "email", user.getEmail());
        this.verificationCodeService.validated(verificationCode);
        this.userService.updateEmail(userDto.getUsername(), user.getEmail());
        return new ResponseEntity(HttpStatus.OK);
    }
    
    private void checkLevel(final User resources) {
        final UserDto user = this.userService.findByName(SecurityUtils.getUsername());
        final Integer currentLevel = Collections.min(this.roleService.findByUsersId(user.getId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
        final Integer optLevel = this.roleService.findByRoles(resources.getRoles());
        if (currentLevel > optLevel) {
            throw new BadRequestException("角色权限不足");
        }
    }
}
